package com.baturu.vin.benz.service.impl;

import com.baturu.vin.benz.consts.BenzConstant;
import com.baturu.vin.benz.dal.dao.*;
import com.baturu.vin.benz.dto.*;
import com.baturu.vin.benz.service.impl.util.BenzStringUtil;
import com.baturu.vin.kit.StreamKit;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 奔驰vin码和配件编码解析器
 *
 * @author 蒋鑫磊
 * @Time 2016/11/18.
 */
@Service("benzCarTypeInfoService")
public class BenzCarTypeInfoService implements InitializingBean {

    @Autowired
    private BenzVinCarTypeInfoDAO benzVinCarTypeInfoDAO;
    @Autowired
    private BenzEPCPartsInfoDAO benzEPCPartsInfoDAO;
    @Autowired
    private BenzPartsMappingDAO benzPartsMappingDAO;
    @Autowired
    private BenzFootNoteInfoDAO benzFootNoteInfoDAO;
    @Autowired
    private BenzImageDAO benzImageDAO;
    @Autowired
    private BenzFootnoteDAO benzFootnoteDAO;
    @Autowired
    private BenzSpringDAO benzSpringDAO;


    private static final Pattern MB_PATTERN = Pattern.compile("[MB]");
    private static final Pattern UPPER_LITTER_PATTERN = Pattern.compile("(?=[A-Z])");

    private static final Map<String, String> DB_NAME_BY_LOCATION = Maps.newHashMap();

    @Override
    public void afterPropertiesSet() {
        Map<String, String> collect = benzVinCarTypeInfoDAO.findDbNameByLocation().stream()
                .collect(Collectors.toMap(k -> k.get("location"), k -> k.get("dbName"), (k1, k2) -> k1));
        DB_NAME_BY_LOCATION.putAll(collect);
    }


    public BenzVinFinInfoDTO getFinInfo(String vin) {
        String vin14 = vin.substring(3, 17);
        List<BenzVinFinInfoDTO> benzVinFinInfoDTOS = benzVinCarTypeInfoDAO.finFinByVin14OrWhc(vin14, vin.substring(0, 3), vin.substring(3, 9), vin.substring(9, 17));
        if (CollectionUtils.isEmpty(benzVinFinInfoDTOS)) {
            return null;
        }
        BenzVinFinInfoDTO benzVinFinInfoDTO = null;
        if (benzVinFinInfoDTOS.size() == 1) {
            benzVinFinInfoDTO = benzVinFinInfoDTOS.get(0);
        } else {
            Optional<BenzVinFinInfoDTO> any = benzVinFinInfoDTOS.stream().filter(b -> b.getVin().equals(vin14)).findAny();
            benzVinFinInfoDTO = any.orElseGet(() -> benzVinFinInfoDTOS.get(0));
        }

        if (benzVinFinInfoDTO == null || StringUtils.isBlank(benzVinFinInfoDTO.getLocation())) {
            return null;
        }
        String dbName = getDBNameByLocation(benzVinFinInfoDTO.getLocation());
        if (StringUtils.isBlank(dbName)) {
            return null;
        }
        benzVinFinInfoDTO.setDbName(dbName);
        return benzVinFinInfoDTO;
    }

    private String getDBNameByLocation(String location) {
        if (BenzConstant.NO_DEAL_LOCATION.contains(location)) {
            return null;
        }
        return DB_NAME_BY_LOCATION.get(location);
    }

    public Set<String> buildSACode(BenzVinFinInfoDTO benzVinFinInfoDTO) {
        String dbName = benzVinFinInfoDTO.getDbName();
        List<String> saCodeList = benzVinCarTypeInfoDAO.findSACodeByDBName(benzVinFinInfoDTO, dbName);
        return BenzStringUtil.getSaCodeSet(saCodeList);
    }

    public BenzVinInfoDTO buildCarTypeInfo(BenzVinFinInfoDTO benzVinFinInfoDTO) {
        //通过Location去定位到需要查询的表
        String dbName = benzVinFinInfoDTO.getDbName();
        BenzVinInfoDTO benzVinInfoDTO = benzVinCarTypeInfoDAO.finCarTypeByDBName(benzVinFinInfoDTO.getWhc(), benzVinFinInfoDTO.getChassbm(), benzVinFinInfoDTO.getChassIdent(), dbName);
        if (benzVinInfoDTO != null) {
            reBuildCarTypeInfo(benzVinInfoDTO);
        }
        return benzVinInfoDTO;
    }

    public Map<String, BenzPartCatInfoDTO> getCatInfoMap(BenzVinInfoDTO benzVinInfoDTO, String appInf) {
        String chassBm = benzVinInfoDTO.getChassbm();
        String engineNum = benzVinInfoDTO.getEngine().length() >= 6 ? benzVinInfoDTO.getEngine().substring(0, 6) : "";
        benzVinInfoDTO.setShortEngine(engineNum);
        String transmissionNum = StringUtils.isNotBlank(benzVinInfoDTO.getTransmission()) && benzVinInfoDTO.getTransmission().length() >= 6 ? benzVinInfoDTO.getTransmission().substring(0, 6) : "";
        benzVinInfoDTO.setShortTransmission(transmissionNum);

        List<BenzPartCatInfoDTO> allCatInfos = getCatInfos(Sets.newHashSet(chassBm, engineNum, transmissionNum), appInf);
        String classNo = BenzStringUtil.getCatClassNo(allCatInfos);
        Map<String, List<BenzPartCatInfoDTO>> collect = allCatInfos.stream().collect(Collectors.groupingBy(b -> b.getType() + b.getSubbm1()));
        List<BenzPartCatInfoDTO> chassCatInfos = collect.get(chassBm);
        List<BenzPartCatInfoDTO> engineCatInfos = collect.get(engineNum);
        List<BenzPartCatInfoDTO> transCatInfos = collect.get(transmissionNum);

        Map<String, BenzPartCatInfoDTO> catInfoMap = Maps.newHashMap();
        if (StringUtils.isNotBlank(chassBm) && CollectionUtils.isNotEmpty(chassCatInfos)) {
            BenzPartCatInfoDTO benzPartCatInfoDTO = filterByClassNo(chassCatInfos, classNo);
            if (Objects.nonNull(benzPartCatInfoDTO)) {
                catInfoMap.put(chassBm, benzPartCatInfoDTO);
            }
        }
        if (StringUtils.isNotBlank(engineNum) && CollectionUtils.isNotEmpty(engineCatInfos)) {
            BenzPartCatInfoDTO benzPartCatInfoDTO = filterByClassNo(engineCatInfos, classNo);
            if (Objects.nonNull(benzPartCatInfoDTO)) {
                catInfoMap.put(engineNum, benzPartCatInfoDTO);
            }
        }
        if (StringUtils.isNotBlank(transmissionNum) && CollectionUtils.isNotEmpty(transCatInfos)) {
            BenzPartCatInfoDTO benzPartCatInfoDTO = filterByClassNo(transCatInfos, classNo);
            if (Objects.nonNull(benzPartCatInfoDTO)) {
                catInfoMap.put(transmissionNum, benzPartCatInfoDTO);
            }
        }
        return catInfoMap;
    }

    public BenzPartCatInfoDTO filterByClassNo(List<BenzPartCatInfoDTO> catInfos, String classNo) {
        return catInfos.stream()
                .filter(catInfo -> catInfo.getClassNo().equals(classNo))
                .findFirst()
                .orElse(null);
    }

    public List<BenzPartCatInfoDTO> getCatInfos(Set<String> typesubbms, String appInf) {
        typesubbms.removeIf(t -> StringUtils.isBlank(t) || t.length() < 6);
        if (CollectionUtils.isEmpty(typesubbms)) {
            return Collections.emptyList();
        }
        List<Map<String, String>> list = typesubbms.stream().map(s -> {
            Map<String, String> map = Maps.newHashMapWithExpectedSize(2);
            map.put("type", s.substring(0, 3));
            map.put("subbm1", s.substring(3, 6));
            return map;
        }).collect(Collectors.toList());
        List<BenzPartCatInfoDTO> catInfoDTOs = benzVinCarTypeInfoDAO.findCarCatNums(list, appInf);
        if (CollectionUtils.isEmpty(catInfoDTOs)) {
            return Collections.emptyList();
        }
        return catInfoDTOs;
    }

    public List<BenzEPCPartsInfoDTO> queryPartsInfoByPartsCode(Set<String> partsCodes) {
        List<BenzEPCPartsInfoDTO> partsInfo = benzEPCPartsInfoDAO.findPartsInfoByCode(partsCodes);
        if (CollectionUtils.isEmpty(partsInfo)) {
            return Collections.emptyList();
        }
        fillEpcPartsName(partsInfo);
        return partsInfo;
    }

    public List<BenzEPCPartsInfoDTO> findPartsByPartsCodeAndCatNum(Set<String> partsCodes, List<String> catNums) {
        Set<String> partsCodesNoColor = partsCodes.stream().map(p -> {
            if (p.length() == 15) {
                p = p.substring(0, 11);
            }
            return p;
        }).collect(Collectors.toSet());
        List<BenzEPCPartsInfoDTO> partsInfo = StreamKit.partitionStream(partsCodesNoColor, 150).flatMap(epcPartsCodes ->
                benzEPCPartsInfoDAO.findPartsByPartsCodeAndCatNum(Sets.newHashSet(epcPartsCodes), catNums).stream()
        ).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(partsInfo)) {
            return Collections.emptyList();
        }
        fillEpcPartsName(partsInfo);
        return partsInfo;
    }

    private void fillEpcPartsName(List<BenzEPCPartsInfoDTO> partsInfo) {
        //epc主名 noun
        Set<String> nounIdx = partsInfo.stream().map(BenzEPCPartsInfoDTO::getPartsNameIdx).collect(Collectors.toSet());
        List<BenzEPCPartsNameDTO> nounByIdx = benzEPCPartsInfoDAO.findNounByIdx(nounIdx, "Z");
        if (CollectionUtils.isEmpty(nounByIdx)) {
            return;
        }
        Map<String, String> nounMap = nounByIdx.stream().collect(Collectors.toMap(BenzEPCPartsNameDTO::getNounIdx, BenzEPCPartsNameDTO::getNoun));
        partsInfo.stream()
                .filter(p -> StringUtils.isNotBlank(p.getPartsNameIdx()) && StringUtils.isNotBlank(nounMap.get(p.getPartsNameIdx())))
                .forEach(p -> p.setPartsName(nounMap.get(p.getPartsNameIdx())));

        //epc主名备注 desc
        Set<String> descIdx = partsInfo.stream().map(BenzEPCPartsInfoDTO::getDescIdx).collect(Collectors.toSet());
        if (CollectionUtils.isEmpty(descIdx)) {
            return;
        }
        Map<String, Set<String>> descIdxMap = descIdx.stream().collect(Collectors.toMap(o -> o, this::dealDescIdx));
        Set<String> allDescIdx = descIdxMap.values().stream().flatMap(Collection::stream).collect(Collectors.toSet());
        if (CollectionUtils.isEmpty(allDescIdx)) {
            return;
        }
        List<BenzEPCPartsNameDTO> descriptions = benzEPCPartsInfoDAO.findDescriptionByIdx(allDescIdx).stream().filter(l -> StringUtils.isNotEmpty(l.getDescription())).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(descriptions)) {
            return;
        }
        Map<String, String> descMap = descriptions.stream().collect(Collectors.toMap(BenzEPCPartsNameDTO::getDescIdx, BenzEPCPartsNameDTO::getDescription));
        partsInfo.stream().filter(p -> StringUtils.isNotBlank(p.getDescIdx()) && CollectionUtils.isNotEmpty(descIdxMap.get(p.getDescIdx()))).forEach(p -> {
            Set<String> descs = descIdxMap.get(p.getDescIdx());
            for (String desc : descs) {
                String description = descMap.get(desc);
                if (StringUtils.isNotBlank(description)) {
                    p.setDescName(description);
                }
            }
        });
    }

    private Set<String> dealDescIdx(String desc) {
        if (StringUtils.isBlank(desc)) {
            return Collections.emptySet();
        }
        Matcher m = MB_PATTERN.matcher(desc);
        String[] split = StringUtils.split(desc, "[MB]");
        for (int i = 0; i < split.length; i++) {
            if (m.find()) {
                split[i] = m.group() + split[i];
            }
        }
        return Sets.newHashSet(split);
    }

    public List<BenzPartsMappingDTO> getRelationDTOs(Set<String> partsNameList, List<String> catNums) {
        return benzPartsMappingDAO.findRelationByBtrPartsName(partsNameList, catNums);
    }

    public List<BenzEPCPartsInfoDTO> findPartsByRepInfo(Collection<BenzEPCPartsInfoDTO> p) {
        return benzEPCPartsInfoDAO.findPartsByRepInfo(p);
    }


    public List<BenzFootNoteInfoDTO> getColorFootNoteInfoDTOs(BenzEPCPartsInfoDTO benzEPCPartsInfoDTO, List<String> footNoteList) {
        Set<String> catNums = Sets.newHashSet(benzEPCPartsInfoDTO.getCatNum());
        Set<String> groupNums = Sets.newHashSet(benzEPCPartsInfoDTO.getGroupNum());
        if (CollectionUtils.isEmpty(catNums) || CollectionUtils.isEmpty(groupNums) || CollectionUtils.isEmpty(footNoteList)) {
            return Collections.emptyList();
        }
        return benzFootNoteInfoDAO.findFootNoteText(Lists.newArrayList(catNums), Lists.newArrayList(groupNums), footNoteList);
    }

    public List<BenzImageDTO> findByPartsCodeByQueryParam(Collection<BenzImageDTO> dtos) {
        return StreamKit.partitionStream(dtos, 150).flatMap(d -> benzImageDAO.findByPartsCodeByQueryParam(d).stream()).collect(Collectors.toList());
    }


    public List<BenzImgPositionDTO> findImgPositionByQueryParam(List<BenzImgPositionDTO> benzImgPositionDTOS) {
        if (CollectionUtils.isEmpty(benzImgPositionDTOS)) {
            return Collections.emptyList();
        }
        return benzImageDAO.findImgPositionByQueryParam(benzImgPositionDTOS);
    }

    private Set<String> splitDescIdx(String descsIdx) {

        String[] ss = UPPER_LITTER_PATTERN.split(descsIdx);
        return Sets.newHashSet(ss);
    }

    public void reBuildCarTypeInfo(BenzVinInfoDTO benzVinInfoDTO) {
        String nullString = "NULL";
        //处理 null字符串等脏数据
        String engine = benzVinInfoDTO.getEngine();
        if (StringUtils.isBlank(engine) || Objects.equals(engine, nullString)) {
            benzVinInfoDTO.setEngine("");
        }
        String transmission = benzVinInfoDTO.getTransmission();
        if (StringUtils.isBlank(transmission) || Objects.equals(transmission, nullString)) {
            benzVinInfoDTO.setTransmission("");
        }
        String steering = benzVinInfoDTO.getSteering();
        if (StringUtils.isBlank(steering) || Objects.equals(steering, nullString)) {
            benzVinInfoDTO.setSteering("");
        }
        String productTime = benzVinInfoDTO.getDeliveryDate();
        if (StringUtils.isBlank(productTime) || Objects.equals(productTime, nullString)) {
            benzVinInfoDTO.setDeliveryDate("");
        }
        String releaseDate = benzVinInfoDTO.getReleaseDate();
        if (StringUtils.isBlank(releaseDate) || Objects.equals(releaseDate, nullString)) {
            benzVinInfoDTO.setReleaseDate("");
        }
        String interior = benzVinInfoDTO.getInterior();
        if (StringUtils.isBlank(interior) || Objects.equals(interior, nullString)) {
            benzVinInfoDTO.setInterior("");
        }
        String paint1 = benzVinInfoDTO.getPaint1();
        if (StringUtils.isBlank(paint1) || Objects.equals(paint1, nullString)) {
            benzVinInfoDTO.setPaint1("");
        }
        String frontAxle = benzVinInfoDTO.getFrontAxle1();
        if (StringUtils.isBlank(frontAxle) || Objects.equals(frontAxle, nullString)) {
            benzVinInfoDTO.setFrontAxle1("");
        }
        String rearAxle = benzVinInfoDTO.getRearAxle1();
        if (StringUtils.isBlank(rearAxle) || Objects.equals(rearAxle, nullString)) {
            benzVinInfoDTO.setRearAxle1("");
        }
        String transferCase = benzVinInfoDTO.getTransferCase();
        if (StringUtils.isBlank(transferCase) || Objects.equals(transferCase, nullString)) {
            benzVinInfoDTO.setTransferCase("");
        }
    }

    public List<BenzFootnoteDTO> findFootnotesByParts(List<BenzEPCPartsInfoDTO> partsInfos) {


        if (CollectionUtils.isEmpty(partsInfos)) {
            return Lists.newArrayList();
        }
        List<BenzEPCPartsInfoForQueryDTO> partsInfoForQueryDTOS = partsInfos.stream().map(p -> BenzEPCPartsInfoForQueryDTO.builder().catNum(p.getCatNum()).groupNum(p.getGroupNum()).build()).distinct().collect(Collectors.toList());
        return benzFootnoteDAO.findFootnotesByParts(partsInfoForQueryDTOS);
    }

    public BenzSpring getBenzSpring(String whc, String chassBm, String chassIdent) {
        return benzSpringDAO.getBenzSpring(whc, chassBm, chassIdent);
    }

}
